/*
 -- IOTA Crypto Core
 --
 -- 2018 by Thomas Pototschnig <microengineer18@gmail.com>
 -- discord: pmaxuw#8292
 -- https://gitlab.com/iccfpga-rv
 --
 -- Permission is hereby granted, free of charge, to any person obtaining
 -- a copy of this software and associated documentation files (the
 -- "Software"), to deal in the Software without restriction, including
 -- without limitation the rights to use, copy, modify, merge, publish,
 -- distribute, sublicense, and/or sell copies of the Software, and to
 -- permit persons to whom the Software is furnished to do so, subject to
 -- the following conditions:
 --
 -- The above copyright notice and this permission notice shall be
 -- included in all copies or substantial portions of the Software.
 --
 -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 -- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 -- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 -- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 -- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 -- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 -- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWAR
 */

/*
 * --------Included Headers--------
 */
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

// printf via SWD
#include "micro-os-plus/diag/trace.h"
#include "debugprintf.h"

#include "riscv.h"
#include "riscv-arch/arch-defines.h"

#include "main.h"
#include "Timer.h"
#include "utils.h"

// Xilinx specific headers
#include "xparameters.h"
#include "xgpio.h"
#include "xdebug.h"

// axi gpio
#include "gpio/gpio.h"
#include "gpio/secure_gpio.h"

#include "fpga/test.h"
#include "fpga/pow.h"
#include "fpga/troika.h"
#include "fpga/fpga.h"
#include "fpga/conversion_fpga.h"

#include "secure/secure.h"

#include "secure/pmp.h"
#include "secure/sbi.h"

#include "qspi/qspi.h"
#include "utils.h"

#include "ssd1331/ssd1331.h"

#include "api/API.h"
#include "api/APIDefault.h"
#include "api/APIPoW.h"
#include "api/APISecureElement.h"
#include "api/APIFlash.h"
#include "api/APIWallet.h"


// profiling
#ifdef GPROF
#include "gmon.h"
#endif

#pragma GCC diagnostic ignored "-Wwrite-strings"

// redirect printf to UART
#ifdef printf
#undef printf
#endif

#define printf xil_printf

Timer timer;

extern API::apiFlagsStruct apiFlags;

void system_halt();
void cpu_startInterrupts();


int main(int argc, char *argv[]) {
	(void) argc;
	(void) argv;
#ifdef DEBUG
	apiFlags.debugSemihostingOutput = true;
#endif

	apiFlags.authMethod = API::AUTH;
//	apiFlags.authMethod = API::HUMAN;

	timer.start();
	cpu_startInterrupts();

	bool ret = false;

	do {
// do one-time-initialisations before switching to user-mode
		if (!(GPIO::gpio_init() == XST_SUCCESS) ||
			!(SecureGPIO::gpio_init() == XST_SUCCESS) ||
			!(SPIFlash::init() == XST_SUCCESS)) {
			break;
		}

	// delay needed for SE after port-initialization
		timer.sleep(500);

		if (apiFlags.authMethod == API::HUMAN) {
			// initialize OLED
			SSD1331::init();
		}

		if (!Secure::init())
			break;

	// disable interrupts while configuring memory protection
		csr_clear(mstatus, MSTATUS_MIE);
		if (!PMP::enable())
			break;
		csr_set(mstatus, MSTATUS_MIE);

		// do some initializations before switching to user-mode
		if (!SBI::init())
			break;

	// switches from machine to user-mode
		SBI::switchUserMode();

	// set some LEDs
		GPIO::setDir(GPIO_LED_LOCK | GPIO_LED_USER, 1);
		GPIO::clrBit(GPIO_LED_LOCK);
		GPIO::setBit(GPIO_LED_USER);

	// set AF for uart
		GPIO::setAF(GPIO_UART_TX | GPIO_UART_RX, 1);

	// register api-commands
		if (!API::registerAPIBase()  ||
			!API::registerAPIPoW() ||
			!API::registerAPIDefault() ||
			!API::registerAPIFlash() ||
			!API::registerAPISecureElement())
			break;
		if (!API::registerAPIWallet())
			break;

		ret = true;
	} while (0);

	if (!ret) {
		printf("{\"code\": 400, \"error\": \"initialization failed\"}\r\n", VERSION);
		system_halt();
	} else {
		printf("{\"code\": 200, \"version\": \"%s\"}\r\n", VERSION);
	}

// start API
	API::runAPI();

	while (1) ;	// is never reached
}

#pragma GCC diagnostic pop

// ----------------------------------------------------------------------------

